"""
定时调度服务
使用 APScheduler 实现工作流的定时调度
"""
import os
from apscheduler.schedulers.background import BackgroundScheduler
from apscheduler.triggers.cron import CronTrigger
from apscheduler.triggers.interval import IntervalTrigger
from apscheduler.events import EVENT_JOB_EXECUTED, EVENT_JOB_ERROR
from sqlalchemy.orm import Session
from datetime import datetime
import logging

from database import SessionLocal
from models.workflow import Workflow
from models.workflow_template import WorkflowTemplate
from models.workflow_instance import WorkflowInstance
from models.workflow_node_execution import WorkflowNodeExecution
from services.workflow_engine import WorkflowEngine

# 配置日志
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)


class SchedulerService:
    """定时调度服务"""

    def __init__(self):
        """初始化调度器"""
        self.scheduler = BackgroundScheduler(timezone='Asia/Shanghai')
        self.scheduler.add_listener(self._job_listener, EVENT_JOB_EXECUTED | EVENT_JOB_ERROR)

        # 🔥 添加工作流执行调度任务（每分钟）
        self.scheduler.add_job(
            func=self._execute_workflows,
            trigger=IntervalTrigger(minutes=int(os.environ.get('WORKFLOW_POLL_INTERVAL_MINUTES', '1'))),
            id="workflow_executor",
            name="工作流执行调度器",
            replace_existing=True
        )
        logger.info("已添加工作流执行调度器（每分钟）")

        # 🔥 添加节点状态轮询任务（每5分钟）
        self.scheduler.add_job(
            func=self._poll_node_status,
            trigger=IntervalTrigger(minutes=int(os.environ.get('WORKFLOW_NODE_POLL_INTERVAL_MINUTES', '5'))),
            id="node_status_poller",
            name="节点状态轮询器",
            replace_existing=True
        )
        logger.info("已添加节点状态轮询器（每5分钟）")

        logger.info("调度器初始化完成")

    def _job_listener(self, event):
        """任务执行监听器"""
        if event.exception:
            logger.error(f"定时任务执行失败: {event.job_id}, 错误: {event.exception}")
        else:
            logger.info(f"定时任务执行成功: {event.job_id}")

    def start(self):
        """启动调度器"""
        if not self.scheduler.running:
            self.scheduler.start()
            logger.info("调度器已启动")
        else:
            logger.warning("调度器已经在运行中")

    def shutdown(self):
        """关闭调度器"""
        if self.scheduler.running:
            self.scheduler.shutdown(wait=False)
            logger.info("调度器已关闭")

    def add_workflow_job(self, workflow_id: int, cron_expression: str):
        """
        添加工作流定时任务

        Args:
            workflow_id: 工作流ID
            cron_expression: Cron 表达式
        """
        job_id = f"workflow_{workflow_id}"

        # 检查任务是否已存在
        if self.scheduler.get_job(job_id):
            logger.info(f"定时任务已存在，先删除: {job_id}")
            self.scheduler.remove_job(job_id)

        # 解析 Cron 表达式
        try:
            trigger = CronTrigger.from_crontab(cron_expression)

            # 添加任务
            self.scheduler.add_job(
                func=self._execute_workflow,
                trigger=trigger,
                id=job_id,
                args=[workflow_id],
                replace_existing=True,
                name=f"定时执行工作流 {workflow_id}"
            )

            logger.info(f"添加定时任务成功: {job_id}, Cron: {cron_expression}")
            return True, f"定时任务添加成功: {job_id}"

        except Exception as e:
            logger.error(f"添加定时任务失败: {job_id}, 错误: {str(e)}")
            return False, f"添加定时任务失败: {str(e)}"

    def remove_workflow_job(self, workflow_id: int):
        """
        移除工作流定时任务

        Args:
            workflow_id: 工作流ID
        """
        job_id = f"workflow_{workflow_id}"

        try:
            if self.scheduler.get_job(job_id):
                self.scheduler.remove_job(job_id)
                logger.info(f"删除定时任务成功: {job_id}")
                return True, "定时任务已删除"
            else:
                logger.warning(f"定时任务不存在: {job_id}")
                return False, "定时任务不存在"

        except Exception as e:
            logger.error(f"删除定时任务失败: {job_id}, 错误: {str(e)}")
            return False, f"删除定时任务失败: {str(e)}"

    # ==================== 实例级别的定时调度（新增） ====================

    def add_instance_job(self, instance_id: int, cron_expression: str):
        """
        添加工作流实例定时任务

        Args:
            instance_id: 工作流实例ID
            cron_expression: Cron 表达式
        """
        job_id = f"instance_{instance_id}"

        # 检查任务是否已存在
        if self.scheduler.get_job(job_id):
            logger.info(f"实例定时任务已存在，先删除: {job_id}")
            self.scheduler.remove_job(job_id)

        # 解析 Cron 表达式
        try:
            trigger = CronTrigger.from_crontab(cron_expression)

            # 添加任务
            self.scheduler.add_job(
                func=self._execute_instance,
                trigger=trigger,
                id=job_id,
                args=[instance_id],
                replace_existing=True,
                name=f"定时执行实例 {instance_id}"
            )

            logger.info(f"添加实例定时任务成功: {job_id}, Cron: {cron_expression}")
            return True, f"实例定时任务添加成功: {job_id}"

        except Exception as e:
            logger.error(f"添加实例定时任务失败: {job_id}, 错误: {str(e)}")
            return False, f"添加实例定时任务失败: {str(e)}"

    def remove_instance_job(self, instance_id: int):
        """
        移除工作流实例定时任务

        Args:
            instance_id: 工作流实例ID
        """
        job_id = f"instance_{instance_id}"

        try:
            if self.scheduler.get_job(job_id):
                self.scheduler.remove_job(job_id)
                logger.info(f"删除实例定时任务成功: {job_id}")
                return True, "实例定时任务已删除"
            else:
                logger.warning(f"实例定时任务不存在: {job_id}")
                return False, "实例定时任务不存在"

        except Exception as e:
            logger.error(f"删除实例定时任务失败: {job_id}, 错误: {str(e)}")
            return False, f"删除实例定时任务失败: {str(e)}"

    def _execute_instance(self, instance_id: int):
        """
        执行工作流实例（定时任务调用）

        注意：这里不是重新运行已有实例，而是基于该实例的配置创建新实例并执行

        Args:
            instance_id: 工作流实例ID
        """
        db = SessionLocal()
        try:
            logger.info(f"实例定时任务触发，准备执行: {instance_id}")

            # 查询实例
            source_instance = db.query(WorkflowInstance).filter(
                WorkflowInstance.id == instance_id,
                WorkflowInstance.deleted_at.is_(None)
            ).first()

            if not source_instance:
                logger.error(f"工作流实例不存在: {instance_id}")
                return

            if not source_instance.cron_enabled:
                logger.warning(f"工作流实例定时调度已禁用: {instance_id}")
                return

            # 基于源实例创建新实例（定时重复执行）
            from services.workflow_instance_service import WorkflowInstanceService
            import json

            input_params = {}
            if source_instance.input_params:
                try:
                    input_params = json.loads(source_instance.input_params)
                except:
                    pass

            success, error_msg, new_instance = WorkflowInstanceService.create_instance_from_template(
                db=db,
                template_id=source_instance.template_id,
                instance_name=f"{source_instance.instance_name}-{datetime.now().strftime('%Y%m%d%H%M%S')}",
                input_params=input_params,
                trigger_type='cron',
                cron_expression=None,  # 新实例不启用定时
                cron_enabled=False,
                created_by=source_instance.created_by
            )

            if success:
                logger.info(f"实例定时任务执行成功: 源实例 {instance_id}, 新实例 {new_instance.id}")
            else:
                logger.error(f"实例定时任务执行失败: 源实例 {instance_id}, 错误: {error_msg}")

        except Exception as e:
            logger.error(f"实例定时任务执行出错: {instance_id}, 错误: {str(e)}")
            import traceback
            traceback.print_exc()

        finally:
            db.close()

    def load_all_instances(self):
        """
        加载所有启用定时调度的工作流实例

        在系统启动时调用（与 load_all_workflows 互补）
        """
        db = SessionLocal()
        try:
            # 查询所有启用定时调度的实例
            instances = db.query(WorkflowInstance).filter(
                WorkflowInstance.cron_enabled == True,
                WorkflowInstance.cron_expression.isnot(None),
                WorkflowInstance.deleted_at.is_(None)
            ).all()

            logger.info(f"找到 {len(instances)} 个启用定时调度的工作流实例")

            success_count = 0
            failed_count = 0

            for instance in instances:
                success, message = self.add_instance_job(
                    instance.id,
                    instance.cron_expression
                )

                if success:
                    success_count += 1
                else:
                    failed_count += 1

            logger.info(f"加载实例定时任务完成: 成功 {success_count}, 失败 {failed_count}")

        except Exception as e:
            logger.error(f"加载实例定时任务失败: {str(e)}")
            import traceback
            traceback.print_exc()

        finally:
            db.close()

    def _execute_workflow(self, workflow_id: int):
        """
        执行工作流（定时任务调用）

        Args:
            workflow_id: 工作流ID
        """
        db = SessionLocal()
        try:
            logger.info(f"定时任务触发，开始执行工作流: {workflow_id}")

            # 查询工作流
            workflow = db.query(Workflow).filter(
                Workflow.id == workflow_id,
                Workflow.deleted_at.is_(None)
            ).first()

            if not workflow:
                logger.error(f"工作流不存在: {workflow_id}")
                return

            if not workflow.cron_enabled:
                logger.warning(f"工作流定时调度已禁用: {workflow_id}")
                return

            # 使用工作流引擎执行
            engine = WorkflowEngine(db)
            success, message, instance = engine.run_workflow(
                workflow_id=workflow_id,
                trigger_type='cron',
                input_params={}
            )

            if success:
                logger.info(f"定时任务执行工作流成功: {workflow_id}, 实例ID: {instance.id}")
            else:
                logger.error(f"定时任务执行工作流失败: {workflow_id}, 错误: {message}")

        except Exception as e:
            logger.error(f"定时任务执行出错: {workflow_id}, 错误: {str(e)}")
            import traceback
            traceback.print_exc()

        finally:
            db.close()

    def load_all_workflows(self):
        """
        加载所有启用定时调度的工作流

        在系统启动时调用
        """
        db = SessionLocal()
        try:
            # 查询所有启用定时调度的工作流
            workflows = db.query(Workflow).filter(
                Workflow.cron_enabled == True,
                Workflow.cron_expression.isnot(None),
                Workflow.deleted_at.is_(None)
            ).all()

            logger.info(f"找到 {len(workflows)} 个启用定时调度的工作流")

            success_count = 0
            failed_count = 0

            for workflow in workflows:
                success, message = self.add_workflow_job(
                    workflow.id,
                    workflow.cron_expression
                )

                if success:
                    success_count += 1
                else:
                    failed_count += 1

            logger.info(f"加载定时任务完成: 成功 {success_count}, 失败 {failed_count}")

        except Exception as e:
            logger.error(f"加载定时任务失败: {str(e)}")
            import traceback
            traceback.print_exc()

        finally:
            db.close()

    def get_job_info(self, workflow_id: int):
        """
        获取定时任务信息

        Args:
            workflow_id: 工作流ID

        Returns:
            任务信息字典或 None
        """
        job_id = f"workflow_{workflow_id}"
        job = self.scheduler.get_job(job_id)

        if not job:
            return None

        return {
            "job_id": job.id,
            "name": job.name,
            "next_run_time": job.next_run_time.isoformat() if job.next_run_time else None,
            "trigger": str(job.trigger)
        }

    def list_all_jobs(self):
        """
        列出所有定时任务

        Returns:
            任务列表
        """
        jobs = self.scheduler.get_jobs()

        result = []
        for job in jobs:
            result.append({
                "job_id": job.id,
                "name": job.name,
                "next_run_time": job.next_run_time.isoformat() if job.next_run_time else None,
                "trigger": str(job.trigger)
            })

        return result

    # ==================== 增强调度功能（新增） ====================

    def _check_schedule_condition(self, instance: WorkflowInstance, db: Session) -> tuple[bool, str]:
        """
        检查实例的调度条件是否满足

        Args:
            instance: 工作流实例
            db: 数据库会话

        Returns:
            (是否满足条件, 不满足的原因)
        """
        import json

        schedule_type = instance.schedule_type if hasattr(instance, 'schedule_type') else 'manual'

        # manual 类型：始终满足条件（立即执行）
        if schedule_type in ('manual', 'create_only'):
            return True, ""

        # scheduled 类型：检查是否到达定时时间
        if schedule_type == 'scheduled':
            if not instance.schedule_config:
                return False, "定时配置为空"

            try:
                config = json.loads(instance.schedule_config)
                scheduled_time_str = config.get('scheduled_time')

                if not scheduled_time_str:
                    return False, "未配置定时时间"

                from datetime import datetime
                scheduled_time = datetime.fromisoformat(scheduled_time_str)
                now = datetime.now()

                # 检查是否已到达定时时间
                if now < scheduled_time:
                    return False, f"尚未到达定时时间 ({scheduled_time_str})"

                # 检查是否已过期（超过7天）
                from datetime import timedelta
                if now > scheduled_time + timedelta(days=7):
                    # 将实例标记为已取消
                    instance.status = WorkflowEngine.STATUS_CANCELLED
                    instance.error_message = f"定时时间已过期（超过7天）: {scheduled_time_str}"
                    db.commit()
                    logger.warning(f"实例 {instance.id} 定时时间已过期，自动取消")
                    return False, "定时时间已过期（超过7天）"

                return True, ""

            except Exception as e:
                logger.error(f"解析定时配置失败: {instance.id}, 错误: {str(e)}")
                return False, f"解析定时配置失败: {str(e)}"

        # dependent 类型：检查所有依赖实例是否已完成
        if schedule_type == 'dependent':
            if not instance.schedule_config:
                return False, "依赖配置为空"

            try:
                config = json.loads(instance.schedule_config)
                dependency_ids = config.get('dependency_instance_ids', [])

                if not dependency_ids:
                    return False, "未配置依赖实例"

                # 查询依赖实例状态
                dependency_instances = db.query(WorkflowInstance).filter(
                    WorkflowInstance.id.in_(dependency_ids),
                    WorkflowInstance.deleted_at.is_(None)
                ).all()

                # 检查依赖实例是否存在
                found_ids = {inst.id for inst in dependency_instances}
                missing_ids = set(dependency_ids) - found_ids
                if missing_ids:
                    return False, f"依赖实例不存在: {list(missing_ids)}"

                # 检查所有依赖实例是否已完成（成功或失败）
                finished_statuses = {
                    WorkflowEngine.STATUS_COMPLETED,
                    WorkflowEngine.STATUS_FAILED
                }

                unfinished_instances = [
                    inst for inst in dependency_instances
                    if inst.status not in finished_statuses
                ]

                if unfinished_instances:
                    unfinished_info = [
                        f"实例{inst.id}({inst.status})"
                        for inst in unfinished_instances
                    ]
                    return False, f"依赖实例尚未完成: {', '.join(unfinished_info)}"

                # 所有依赖实例已完成
                return True, ""

            except Exception as e:
                logger.error(f"检查依赖条件失败: {instance.id}, 错误: {str(e)}")
                return False, f"检查依赖条件失败: {str(e)}"

        # cron 类型：由 APScheduler 管理，不需要在这里检查
        # （CRON 任务会直接触发 _execute_instance 创建新实例）
        if schedule_type == 'cron':
            return True, ""

        # 未知类型
        return False, f"未知的调度类型: {schedule_type}"

    # ==================== 异步工作流调度方法 ====================

    def _execute_workflows(self):
        """
        扫描需要执行的工作流实例

        目标状态：
        - init: 新创建的实例（已启动）
        - running: 正在运行的实例
        - waiting: 等待节点轮询结果的实例

        注意：不包括 STATUS_NOT_STARTED（未运行）状态，这些实例需要用户手动启动
        """
        db = SessionLocal()
        try:
            # 查询需要调度的工作流实例
            # 注意：STATUS_NOT_STARTED 不在此列表中，因此不会被自动调度
            instances = db.query(WorkflowInstance).filter(
                WorkflowInstance.status.in_([
                    WorkflowEngine.STATUS_INIT,
                    WorkflowEngine.STATUS_RUNNING,
                    WorkflowEngine.STATUS_WAITING
                ])
            ).all()

            if instances:
                logger.info(f"[WorkflowExecutor] 扫描到 {len(instances)} 个待处理工作流实例")

            for instance in instances:
                try:
                    # 检查调度条件
                    condition_met, reason = self._check_schedule_condition(instance, db)

                    if not condition_met:
                        # 条件不满足，跳过此实例（不记录日志以减少噪音）
                        # 只有在初始化状态且等待时间过长时才警告
                        if instance.status == WorkflowEngine.STATUS_INIT:
                            from datetime import datetime, timedelta
                            if instance.created_at < datetime.now() - timedelta(hours=24):
                                logger.warning(f"[WorkflowExecutor] 实例 {instance.id} 等待超过24小时: {reason}")
                        continue

                    # 条件满足，执行工作流步骤
                    engine = WorkflowEngine(db)
                    should_continue = engine.execute_workflow_step(instance.id)

                    # 如果不需要继续调度，记录日志
                    if not should_continue:
                        logger.info(f"[WorkflowExecutor] 工作流 {instance.id} 执行结束或暂停")

                except Exception as e:
                    logger.error(f"[WorkflowExecutor] 执行工作流 {instance.id} 异常: {str(e)}")
                    import traceback
                    traceback.print_exc()

        except Exception as e:
            logger.error(f"[WorkflowExecutor] 调度器执行异常: {str(e)}")
            import traceback
            traceback.print_exc()

        finally:
            db.close()

    def _poll_node_status(self):
        """
        轮询节点执行状态

        目标状态：
        - submitted: 已提交到外部系统
        - running: 外部系统确认运行中
        """
        db = SessionLocal()
        try:
            # 查询需要轮询的节点
            now = datetime.now()
            executions = db.query(WorkflowNodeExecution).filter(
                WorkflowNodeExecution.status.in_([
                    WorkflowEngine.NODE_STATUS_SUBMITTED,
                    WorkflowEngine.NODE_STATUS_RUNNING
                ]),
                # WorkflowNodeExecution.next_poll_at <= now
            ).all()

            if executions:
                logger.info(f"[NodePoller] 扫描到 {len(executions)} 个待轮询节点")

            for execution in executions:
                try:
                    instance = db.query(WorkflowInstance).filter(
                        WorkflowInstance.id == execution.workflow_instance_id,
                        WorkflowInstance.deleted_at.is_(None)
                    )
                    if not instance:
                        logger.info(f"[NodePoller] 工作流实例 id {execution.workflow_instance_id} 状态异常：{execution.id} 轮询结束")
                    engine = WorkflowEngine(db)
                    should_continue = engine.poll_node_status(execution.id)

                    if not should_continue:
                        logger.info(f"[NodePoller] 节点 {execution.id} 轮询结束")

                except Exception as e:
                    logger.error(f"[NodePoller] 轮询节点 {execution.id} 异常: {str(e)}")
                    import traceback
                    traceback.print_exc()

        except Exception as e:
            logger.error(f"[NodePoller] 调度器执行异常: {str(e)}")
            import traceback
            traceback.print_exc()

        finally:
            db.close()


# 全局调度器实例
scheduler_service = SchedulerService()
